package com.huanju.chajianhuatest;
import android.app.Instrumentation;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
/**
* @author 刘镓旗
* @date 17/2/21
*/
public class MyHookHelper {
/**
* 加载插件
* @param loader
*/
public static void inject(DexClassLoader loader){
//拿到本应用的ClassLoader
PathClassLoader pathLoader = (PathClassLoader) MyApplication.getContext().getClassLoader();
try {
//获取宿主pathList
Object suZhuPathList = getPathList(pathLoader);
Object chaJianPathList = getPathList(loader);
Object dexElements = combineArray(
//获取本应用ClassLoader中的dex数组
getDexElements(suZhuPathList),
//获取插件CassLoader中的dex数组
getDexElements(chaJianPathList));
// //获取合并后的pathList
// Object pathList = getPathList(pathLoader);
//将合并的pathList设置到本应用的ClassLoader
setField(suZhuPathList, suZhuPathList.getClass(), "dexElements", dexElements);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取pathList字段
* @param baseDexClassLoader 需要获取pathList字段的ClassLoader
* @return 返回pathList字段
* @throws IllegalArgumentException
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
private static Object getPathList(Object baseDexClassLoader)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
// ClassLoader bc = (ClassLoader)baseDexClassLoader;
//通过这个ClassLoader获取pathList字段
return getField(baseDexClassLoader, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
/**
* 反射需要获取的字段
* @param obj 需要字段获取的对象
* @param cl 需要获取字段的类
* @param field 需要获取的字段名称
* @return 获取后的字段
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static Object getField(Object obj, Class<?> cl, String field)
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
//反射需要获取的字段
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
return localField.get(obj);
}
/**
* 获取DexElements
* @param paramObject 需要获取字段的对象
* @return 返回获取的字段
* @throws IllegalArgumentException
* @throws NoSuchFieldException
* @throws IllegalAccessException
*/
private static Object getDexElements(Object paramObject)
throws IllegalArgumentException, NoSuchFieldException, IllegalAccessException {
return getField(paramObject, paramObject.getClass(), "dexElements");
}
/**
* 反射需要设置字段的类并设置新字段
* @param obj
* @param cl
* @param field
* @param value
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
private static void setField(Object obj, Class<?> cl, String field,
Object value) throws NoSuchFieldException,
IllegalArgumentException, IllegalAccessException {
Field localField = cl.getDeclaredField(field);
localField.setAccessible(true);
localField.set(obj, value);
}
/**
* 合成dex数组
* @param suzhu 宿主应用的dex数组
* @param chajian 插件应用的dex数组
* @return
*/
private static Object combineArray(Object suzhu, Object chajian) {
//获取原数组类型
Class<?> localClass = suzhu.getClass().getComponentType();
//获取原数组长度
int i = Array.getLength(suzhu);
//插件数组加上原数组的长度
int j = i + Array.getLength(chajian);
//创建一个新的数组用来存储
Object result = Array.newInstance(localClass, j);
//一个个的将dex文件设置到新数组中
for (int k = 0; k < j; ++k) {
if (k < i) {
Array.set(result, k, Array.get(suzhu, k));
} else {
Array.set(result, k, Array.get(chajian, k - i));
}
}
return result;
}
/**
* hookActivity的Resource,如果想马上生效需要针对使用Resource的组件hook
* @param context
*/
public static void hookActivityResource(Context context){
try {
//获取ActiivtiyThread类
Class<?> mActivityThreadClass = Class.forName("android.app.ActivityThread");
//获取当前ActivityThread
Method currentActivityThread = mActivityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThread.setAccessible(true);
Object mCurrentActivityThread = currentActivityThread.invoke(null);
//获取mInstrumentation字段
Field mInstrumentationField = mActivityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation baseInstrumentation = (Instrumentation) mInstrumentationField.get(mCurrentActivityThread);
//设置我们自己的mInstrumentation
Instrumentation proxy = new MyInstrumentation(baseInstrumentation,context);
//替换
mInstrumentationField.set(mCurrentActivityThread,proxy);
Log.e("Main","替换Resource成功");
} catch (Exception e) {
Log.e("Main","替换Resource失败 = " + e.getMessage());
e.printStackTrace();
}
}
}